import WebGl from '@/modules/three';
// import fontJson from 'three/examples/fonts/helvetiker_regular.typeface.json'
const fontJson = require('three/examples/fonts/helvetiker_regular.typeface.json');

const { Three, TWEEN, dat, Stats, OrbitControls } = WebGl;

class WebGlControl {
  private container: any;
  private camera: any; //透视相机
  private orthocam: any; //正交相机
  private scene: any;
  private renderer: any;
  private stats: any;
  private animateFrame: any;
  private tween: any;
  private controls: any;
  private clock: any = new Three.Clock();

  private Geometrys: {
    cubeGeometry?: any;
    ballGeometry?: any;
    haloGeometry?: any;
  } = {}; //保存所有Geometry，方便统一销毁
  private Materials: {
    cubeMaterial?: any;
    ballMaterial?: any;
    haloMaterial?: any;
  } = {}; //保存所有Material，方便统一销毁
  private Meshs: {
    cube?: any;
    ball?: any;
    halo?: any;
  } = {};
  private particleSystem: any;

  private GuiInit: any = null;
  private guiData: any = {
    // x: 0.005,
    // color: '#000000',
    autoRotate: true,
    blurSize: 0.5,
    radiusSize: 0.7,
  };

  public init = (id: string) => {
    // if(this.scene) {return}
    this.container = document.getElementById(id);
    let width = this.container.clientWidth,
      height = this.container.clientHeight;

    this.scene = new Three.Scene();

    this.camera = new Three.PerspectiveCamera(60, width / height, 1, 500);
    this.camera.position.set(0, 0, 80);
    this.camera.lookAt(this.scene.position);

    const ortW = width / 15,
      ortH = height / 15;
    this.orthocam = new Three.OrthographicCamera(
      ortW / -2,
      ortW / 2,
      ortH / 2,
      ortH / -2,
      1,
      1000,
    ); //正交相机
    this.orthocam.position.set(0, 0, 80);
    this.orthocam.layers.set(1);
    this.orthocam.layers.enable(2);

    this.renderer = new Three.WebGLRenderer({
      // alpha: true
    });
    this.renderer.autoClear = false;
    this.renderer.setClearColor(0xffffff);
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.renderer.setSize(width, height);
    this.container.appendChild(this.renderer.domElement);

    this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    this.controls.enableDamping = true; //阻尼
    this.controls.dampingFactor = 0.1; //阻尼

    this.addThings();
    this.addLight(); //+光源
    this.animate(); //开始动
    this.initStats();

    this.initGui();
  };
  public destroy = () => {
    this.toggleGui(0);
    Object.values(this.Geometrys).forEach((e) => {
      e.dispose();
    });
    Object.values(this.Materials).forEach((e) => {
      e.dispose();
    });
    this.scene.clear();
    this.renderer.dispose();
    this.renderer.forceContextLoss();
    this.camera = null;
    this.scene = null;
    this.renderer = null;
    this.GuiInit = null;
    this.stats = null;
    if (this.tween) {
      this.tween.stop();
      this.tween = null;
    }
    cancelAnimationFrame(this.animateFrame);
  };

  private initStats = () => {
    this.stats = new Stats();
    this.stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
    this.stats.dom.style.position = 'absolute';
    this.container.appendChild(this.stats.dom);
  };

  public initGui = () => {
    if (this.GuiInit) {
      this.toggleGui();
      return;
    }
    this.GuiInit = new dat.GUI({
      // autoPlace: false
    });

    this.GuiInit.domElement.style.position = 'absolute';
    this.GuiInit.domElement.style.right = '10px';
    this.GuiInit.domElement.style.top = '10px';

    let GuiController = this.GuiInit.addFolder('控制窗口');
    // GuiController.add(this.guiData, 'x', -0.1, 0.1, 0.005).name('自转速度');
    // GuiController.addColor(this.guiData, 'color').name('背景颜色');
    GuiController.add(this.guiData, 'blurSize', 0, 1, 0.005)
      .name('blurSize')
      .onChange((val) => {
        this.Materials.haloMaterial.uniforms.blur_size.value = val;
      });
    GuiController.add(this.guiData, 'radiusSize', 0, 1, 0.005)
      .name('radiusSize')
      .onChange((val) => {
        this.Materials.haloMaterial.uniforms.radius_size.value = val;
      });

    GuiController.open();

    // let cubeGui = this.GuiInit.addFolder("cube position");
    // cubeGui.open()
  };

  public toggleGui = (state: any = null) => {
    if (!this.GuiInit || !this.GuiInit.domElement) {
      return;
    }
    let display = this.GuiInit.domElement.style.display == 'none' ? '' : 'none';
    if (state == 0) {
      display = 'none';
    }
    this.GuiInit.domElement.style.display = display;
  };

  private addLight = () => {
    let ambi = new Three.AmbientLight(0x686868);
    // ambi.layers.set(1)
    this.scene.add(ambi);
    let dirLight = new Three.DirectionalLight(0xffffff);
    dirLight.position.set(1, 1, 1);
    dirLight.intensity = 0.6;
    // dirLight.layers.set(1)
    this.scene.add(dirLight);
    // let light = new Three.PointLight( 0xFFFF00 );
    // light.position.set( -100, 100, 0 );
    // this.scene.add(light);
  };

  private addThings = () => {
    // const axesHelper = new Three.AxesHelper( 800 );
    // this.scene.add( axesHelper )  //添加坐标轴；红线是X轴，绿线是Y轴，蓝线是Z轴

    /** 立方体 **/
    this.Geometrys.cubeGeometry = new Three.BoxGeometry(10, 10, 10);
    this.Materials.cubeMaterial = new Three.MeshPhongMaterial({
      color: 0xffffff,
    });
    this.Meshs.cube = new Three.Mesh(
      this.Geometrys.cubeGeometry,
      this.Materials.cubeMaterial,
    );
    // this.Meshs.cube.position.set(0, -15, 0)
    this.Meshs.cube.rotation.set(0.2, 0.2, 0);
    // this.Meshs.cube.layers.set(1)
    this.scene.add(this.Meshs.cube);
    /** 立方体 **/

    /** 球 **/
    this.Geometrys.ballGeometry = new Three.SphereGeometry(10, 32, 16);
    // this.Materials.ballMaterial = new Three.MeshPhongMaterial({
    //   color: 0xffea88,
    // });
    this.Materials.ballMaterial = new Three.ShaderMaterial({
      uniforms: {
        val: { value: 0.0 },
        color1: {
          value: new Three.Color('#e2b985'),
        },
        color2: {
          value: new Three.Color('#8d6eb4'),
        },
        // color3: {
        //   value: new Three.Color('#a1a2d9'),
        // },
      },
      vertexShader: `
        varying vec2 vUv;
        void main() {
          vUv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
        }
      `,
      fragmentShader: `
        uniform vec3 color1;
        uniform vec3 color2;
        uniform vec3 color3;
        uniform float val;
        varying vec2 vUv;

        void main() {
          float y = vUv.y;
          // 发光的强度
          float glowIntensity = 1.5;

          vec3 co1 = color1.rgb * glowIntensity;
          vec3 co2 = color2.rgb * glowIntensity;
          // vec3 co3 = color3.rgb * glowIntensity;
          vec3 gradientColor;
          // if (y <= 0.5) {
            gradientColor = mix(co1, co2, y * 2.0);
          // } else {
          //   gradientColor = mix(co2, co3, y * 2.0 - 1.0);
          // }

          vec4 finalColor = vec4(gradientColor, 1.0 * 1.0);
          gl_FragColor = finalColor;
        }
      `,
      // lights: true
      // wireframe: true
    });
    this.Meshs.ball = new Three.Mesh(
      this.Geometrys.ballGeometry,
      this.Materials.ballMaterial,
    );
    this.Meshs.ball.layers.set(1);
    this.scene.add(this.Meshs.ball);
    /** 球 **/

    this.addCircle();

    this.initTween();
  };

  private addCircle = () => {
    // this.Materials.haloMaterial = new Three.MeshBasicMaterial({
    //   color: 0xffcf24
    // })
    this.Materials.haloMaterial = new Three.ShaderMaterial({
      uniforms: {
        color_1: {
          value: new Three.Color('#6158db'),
        },
        color_2: {
          value: new Three.Color('#c23de6'),
        },
        blur_size: {
          value: 0.5,
        },
        radius_size: {
          value: 0.7,
        },
        opacity: {
          value: 1.0,
        },
      },
      vertexShader: `
        #define GLSLIFY 1
        varying vec2 vUv;
        void main() {
          vUv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
      `,
      fragmentShader: `
        #define GLSLIFY 1
        /* Easing Sine InOut equation */
        /* Adapted from Robert Penner easing equations */
        #define PI 3.141592653589793
        
        float easeSineInOut(float t) {
          return -0.5 * (cos(PI * t * 2.0) - 1.0);
        }
        
        float circle(in vec2 _uv, in float _radius, in float _blur){
          return 1. - smoothstep(_radius-(_radius*_blur), _radius+(_radius*_blur), dot(_uv,_uv)*4.0);
        }
        
        uniform vec3 color_1;
        uniform vec3 color_2;
        
        uniform float blur_size;
        uniform float radius_size;
        uniform float opacity;
        
        varying vec2 vUv;
        
        mat2 rotate(float _a) {
          return mat2(cos(_a), -sin(_a), sin(_a), cos(_a));
        }
        
        vec2 start = vec2(0.25, 0.8);
        vec2 end   = vec2(0.65, 0.1);
        
        void main() {
          vec2 uv = vUv - 0.5;
      
          float c = circle(uv, radius_size, blur_size);
      
          // Color
          vec2 gDir = end - start;
          float lenSq = dot(gDir, gDir * 0.8);
          vec2 relCoord = uv + 0.5 - start;
          float pct = dot(relCoord, gDir) / lenSq;
      
          vec3 color = mix(color_1, color_2, pct);
          vec4 finalColor = vec4(color, c * opacity);
      
          gl_FragColor = finalColor;
        }
      `,
      transparent: !0,
    });
    (this.Meshs.halo = new Three.Mesh(
      new Three.CircleBufferGeometry(40, 8),
      this.Materials.haloMaterial,
    )),
      (this.Meshs.halo.name = 'halo'),
      // this.Meshs.halo.scale.set(y, y, y),
      this.scene.add(this.Meshs.halo);
  };

  private initTween = () => {
    // var pos: any = { val: 1 };
    // this.tween = new TWEEN.Tween(pos)
    //   .to({ val: 0 }, 1000)
    //   .easing(TWEEN.Easing.Quadratic.InOut)
    //   .delay(200)
    //   .onUpdate(callback);
    // let tweenBack = new TWEEN.Tween(pos)
    //   .to({ val: 1 }, 1000)
    //   .easing(TWEEN.Easing.Quadratic.InOut)
    //   .delay(200)
    //   .onUpdate(callback);
    // this.tween.chain(tweenBack);
    // tweenBack.chain(this.tween);
    // this.tween.start();
    // let that = this;
    // function callback(res) {
    //   that.Materials.starsMaterial.uniforms.val.value = res.val;
    // }
  };

  private animate = () => {
    // if(!this.renderer){return;}
    if (this.stats) {
      this.stats.begin();
    }
    const delta = this.clock.getDelta();

    this.render();
    TWEEN.update();
    this.controls.update();

    const r = Date.now() * 0.0015;
    const x = 20 * Math.sin(r);
    const y = -20 * Math.sin(r * 2);
    this.Meshs.ball.position.set(x, y, 0);

    if (this.stats) {
      this.stats.end();
    }
    this.animateFrame = requestAnimationFrame(this.animate);
  };
  private render = () => {
    let width = this.container.clientWidth,
      height = this.container.clientHeight;

    // this.orthocam.layers.set(2),
    // this.renderer.setRenderTarget(this.renderTarget),
    this.renderer.clear();
    // this.renderer.render(this.scene, this.orthocam)
    this.orthocam.layers.set(1);
    // this.renderer.setRenderTarget(null)
    // this.renderer.setClearColor('#aeaeae', 1.0);
    // this.scene.background = '#aeaeae'
    this.renderer.setViewport(width - 100 - 10, height - 100 - 10, 100, 100);
    this.renderer.render(this.scene, this.orthocam);

    // this.renderer.clear(true,true,false)
    // this.renderer.setClearColor('#fff', 1.0)
    this.renderer.setViewport(0, 0, width, height);
    // this.renderer.clearDepth()
    this.renderer.render(this.scene, this.camera);
    // this.renderer.clearDepth()
  };
}

const webGlControl = new WebGlControl();
export default webGlControl;
